import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import pandas as pd


class Course:
    def __init__(self, course_name, credits, grade, is_repeated=False):
        self.course_name = course_name
        self.credits = credits
        self.grade = grade
        self.is_repeated = is_repeated

    def __str__(self):
        return f"课程名称：{self.course_name}, 学分：{self.credits}, 成绩：{self.grade}, 是否重修：{self.is_repeated}"


class StudentInfo:
    def __init__(self, student_id, name, department, class_name):
        self.student_id = student_id
        self.name = name
        self.department = department
        self.class_name = class_name


class AcademicData:
    def __init__(self):
        self.courses_taken = []  # 已修课程信息列表
        self.gpa = 0.0  # 平均学分绩点
        self.failed_credits = 0  # 累计不及格课程总学分

    def add_course(self, course):
        # 检查课程是否已经存在
        existing_course = next((c for c in self.courses_taken if c.course_name == course.course_name), None)
        if existing_course:
            # 如果课程已存在，更新课程信息
            existing_course.credits = course.credits
            existing_course.grade = course.grade
            existing_course.is_repeated = course.is_repeated
        else:
            # 如果课程不存在，添加新课程
            self.courses_taken.append(course)
        # 更新GPA和不及格课程总学分
        self.update_gpa()
        self.update_failed_credits()

    def update_gpa(self):
        total_points = 0
        total_credits = 0
        for course in self.courses_taken:
            if course.grade >= 60:  # 假设60分为及格
                # 根据成绩计算绩点，这里简单示例，实际应根据学校规定计算
                if course.grade >= 90:
                    points = 4.0
                elif course.grade >= 80:
                    points = 3.0
                elif course.grade >= 70:
                    points = 2.0
                elif course.grade >= 60:
                    points = 1.0
                else:
                    points = 0.0
                total_points += points * course.credits
                total_credits += course.credits
        if total_credits > 0:
            self.gpa = total_points / total_credits
        else:
            self.gpa = 0.0

    def update_failed_credits(self):
        self.failed_credits = sum(course.credits for course in self.courses_taken if course.grade < 60)


class Student:
    def __init__(self, student_info, academic_data):
        self.student_info = student_info
        self.academic_data = academic_data

    def __str__(self):
        return f"学号：{self.student_info.student_id}, 姓名：{self.student_info.name}, 所在院系：{self.student_info.department}, 班级：{self.student_info.class_name}, " \
               f"平均学分绩点：{self.academic_data.gpa:.2f}, 累计不及格课程：{self.academic_data.failed_credits}"


class StudentManager:
    def __init__(self):
        self.students = {}  # 学生信息字典，以学号为键

    def add_student(self, student):
        self.students[student.student_info.student_id] = student

    def get_student_info(self, student_id):
        return self.students.get(student_id, None)

    def add_course_to_student(self, student_id, course):
        student = self.get_student_info(student_id)
        if student:
            student.academic_data.add_course(course)

    def export_to_excel(self, file_path):
        data = []
        for student in self.students.values():
            for course in student.academic_data.courses_taken:
                data.append([
                    student.student_info.student_id,
                    student.student_info.name,
                    student.student_info.department,
                    student.student_info.class_name,
                    course.course_name,
                    course.credits,
                    course.grade,
                    "是" if course.is_repeated else "否"
                ])
        df = pd.DataFrame(data, columns=["学号", "姓名", "院系", "班级", "课程名称", "学分", "成绩", "是否重修"])
        df.to_excel(file_path, index=False)


class StudentGUI:
    def __init__(self, master, data_file):
        self.master = master
        self.master.title("学生学情管理系统")
        self.master.geometry("800x600")
        self.student_manager = StudentManager()
        self.data_file = data_file

        # 创建学生信息表格
        self.student_tree = ttk.Treeview(master, columns=("学号", "姓名", "院系", "班级"), show="headings")
        self.student_tree.heading("学号", text="学号")
        self.student_tree.heading("姓名", text="姓名")
        self.student_tree.heading("院系", text="院系")
        self.student_tree.heading("班级", text="班级")
        self.student_tree.column("学号", width=100)
        self.student_tree.column("姓名", width=100)
        self.student_tree.column("院系", width=150)
        self.student_tree.column("班级", width=100)
        self.student_tree.pack(pady=10, fill=tk.BOTH, expand=True)

        # 绑定选择学生事件
        self.student_tree.bind("<<TreeviewSelect>>", self.show_student_courses)
 
        # 创建课程信息表格
        self.course_tree = ttk.Treeview(master, columns=("课程名称", "学分", "成绩", "是否重修"), show="headings")
        self.course_tree.heading("课程名称", text="课程名称")
        self.course_tree.heading("学分", text="学分")
        self.course_tree.heading("成绩", text="成绩")
        self.course_tree.heading("是否重修", text="是否重修")
        self.course_tree.column("课程名称", width=200)
        self.course_tree.column("学分", width=50)
        self.course_tree.column("成绩", width=50)
        self.course_tree.column("是否重修", width=100)
        self.course_tree.pack(pady=10, fill=tk.BOTH, expand=True)

        # 从指定源文件中读取表格数据
        self.load_data_from_file(self.data_file)

        # 添加学生管理按钮
        self.add_button = tk.Button(master, text="学生管理", command=self.open_student_management_window)
        self.add_button.pack(pady=10)

    def load_data_from_file(self, file_path):
        df = pd.read_excel(file_path)
        grouped = df.groupby('学号')
        for student_id, group in grouped:
            name = group['姓名'].iloc[0]
            department = group['院系'].iloc[0]
            class_name = group['班级'].iloc[0]
            # 创建学生信息对象
            student_info = StudentInfo(student_id, name, department, class_name)
            # 创建学术数据对象
            academic_data = AcademicData()
            # 遍历该学生的所有课程，添加到academic_data
            for index, row in group.iterrows():
                course_name = row['课程名称']
                course_credits = row['学分']
                course_grade = row['成绩']
                course_is_repeated = row['是否重修'] == '是'
                course = Course(course_name, course_credits, course_grade, course_is_repeated)
                academic_data.add_course(course)
            # 创建学生对象并添加到学生管理器中
            student = Student(student_info, academic_data)
            self.student_manager.add_student(student)
        # 将学生信息填充到学生信息表格中
        for student in self.student_manager.students.values():
            self.student_tree.insert("", "end", values=(student.student_info.student_id, student.student_info.name,
                                                       student.student_info.department, student.student_info.class_name))

    def show_student_courses(self, event):
        selected_item = self.student_tree.selection()
        if selected_item:
            item = self.student_tree.item(selected_item)
            student_id = item['values'][0]
            student = self.student_manager.get_student_info(student_id)
            if student:
                # 清空课程信息表格
                self.course_tree.delete(*self.course_tree.get_children())
                # 填充课程信息
                for course in student.academic_data.courses_taken:
                    self.course_tree.insert("", "end", values=(course.course_name, course.credits,
                                                              course.grade, "是" if course.is_repeated else "否"))
    
    def open_student_management_window(self):
        window = tk.Toplevel(self.master)
        window.title("学生管理")
        window.geometry("600x400")

        # 学生信息输入区
        input_frame = ttk.LabelFrame(window, text="学生信息输入")
        input_frame.pack(pady=10, fill=tk.X)

        tk.Label(input_frame, text="学号:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
        self.entry_student_id = tk.Entry(input_frame)
        self.entry_student_id.grid(row=0, column=1, padx=5, pady=5, sticky=tk.W)
 
        tk.Label(input_frame, text="姓名:").grid(row=1, column=0, padx=5, pady=5, sticky=tk.W)
        self.entry_name = tk.Entry(input_frame)
        self.entry_name.grid(row=1, column=1, padx=5, pady=5, sticky=tk.W)

        tk.Label(input_frame, text="院系:").grid(row=2, column=0, padx=5, pady=5, sticky=tk.W)
        self.entry_department = tk.Entry(input_frame)
        self.entry_department.grid(row=2, column=1, padx=5, pady=5, sticky=tk.W)

        tk.Label(input_frame, text="班级:").grid(row=3, column=0, padx=5, pady=5, sticky=tk.W)
        self.entry_class = tk.Entry(input_frame)
        self.entry_class.grid(row=3, column=1, padx=5, pady=5, sticky=tk.W)

        # 查询学生信息区
        query_frame = ttk.LabelFrame(window, text="查询学生信息")
        query_frame.pack(pady=10, fill=tk.X)

        tk.Label(query_frame, text="学号:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
        self.entry_query_student_id = tk.Entry(query_frame)
        self.entry_query_student_id.grid(row=0, column=1, padx=5, pady=5, sticky=tk.W)

        # 操作按钮区
        button_frame = ttk.Frame(window)
        button_frame.pack(pady=10)

        tk.Button(button_frame, text="添加学生", command=self.add_student).grid(row=0, column=0, padx=5)
        tk.Button(button_frame, text="录入学生成绩", command=self.add_student_grade).grid(row=0, column=1, padx=5)
        tk.Button(button_frame, text="导入表格", command=self.import_table).grid(row=0, column=2, padx=5)
        tk.Button(button_frame, text="导出表格", command=self.export_table).grid(row=0, column=3, padx=5)
        tk.Button(button_frame, text="清空输入", command=self.clear_input).grid(row=0, column=4, padx=5)

        # 查询结果区
        result_frame = ttk.LabelFrame(window, text="查询结果")
        result_frame.pack(pady=10, fill=tk.BOTH, expand=True)
    
        self.result_text = tk.Text(result_frame, wrap=tk.NONE)
        self.result_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        scrollbar = ttk.Scrollbar(result_frame, command=self.result_text.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.result_text.config(yscrollcommand=scrollbar.set)

    def add_student(self):
        student_id = self.entry_student_id.get()
        name = self.entry_name.get()
        department = self.entry_department.get()
        class_name = self.entry_class.get()

        if student_id and name and department and class_name:
            student_info = StudentInfo(student_id, name, department, class_name)
            academic_data = AcademicData()
            student = Student(student_info, academic_data)
            self.student_manager.add_student(student)
            messagebox.showinfo("成功", "学生信息添加成功！")

            # 插入新学生数据到Treeview
            self.student_tree.insert("", "end", values=(student.student_info.student_id, 
                                                         student.student_info.name, 
                                                         student.student_info.department, 
                                                         student.student_info.class_name))

            # 更新GUI
            self.student_tree.update_idletasks()
        else:
            messagebox.showwarning("警告", "请输入完整的学生信息！")

    def add_student_grade(self):
        student_id = self.entry_student_id.get()
        student = self.student_manager.get_student_info(student_id)
        if student:
            def submit_grade():
                course_name = entry_course_name.get()
                credits = entry_credits.get()
                grade = entry_grade.get()
                is_repeated = entry_is_repeated.get()
 
                if course_name and credits and grade:
                    try:
                        credits = float(credits)
                        grade = float(grade)
                        is_repeated = True if is_repeated == '1' else False
                        course = Course(course_name, credits, grade, is_repeated)
                        self.student_manager.add_course_to_student(student_id, course)
                        messagebox.showinfo("成功", "学生成绩录入成功！")
                        grade_window.destroy()
                    except ValueError:
                        messagebox.showwarning("警告", "请输入正确的学分和成绩！")
                else:
                    messagebox.showwarning("警告", "请输入完整的课程信息！")

            grade_window = tk.Toplevel(self.master)
            grade_window.title("录入学生成绩")
            grade_window.geometry("300x200")

            tk.Label(grade_window, text="课程名称:").pack(pady=5)
            entry_course_name = tk.Entry(grade_window)
            entry_course_name.pack(pady=5)

            tk.Label(grade_window, text="学分:").pack(pady=5)
            entry_credits = tk.Entry(grade_window)
            entry_credits.pack(pady=5)

            tk.Label(grade_window, text="成绩:").pack(pady=5)
            entry_grade = tk.Entry(grade_window)
            entry_grade.pack(pady=5)

            tk.Label(grade_window, text="是否重修(0或1):").pack(pady=5)
            entry_is_repeated = tk.Entry(grade_window)
            entry_is_repeated.pack(pady=5)

            tk.Button(grade_window, text="确定", command=submit_grade).pack(pady=10)
        else:
            messagebox.showwarning("警告", "学生信息不存在，请先添加学生！")

    def query_student(self):
        student_id = self.entry_query_student_id.get()
        student = self.student_manager.get_student_info(student_id)
        if student:
            self.result_text.delete(1.0, tk.END)
            self.result_text.insert(tk.END, f"学号: {student.student_info.student_id}\n")
            self.result_text.insert(tk.END, f"姓名: {student.student_info.name}\n")
            self.result_text.insert(tk.END, f"院系: {student.student_info.department}\n")
            self.result_text.insert(tk.END, f"班级: {student.student_info.class_name}\n")
            self.result_text.insert(tk.END, f"GPA: {student.academic_data.gpa:.2f}\n")
            self.result_text.insert(tk.END, f"累计不及格课程学分: {student.academic_data.failed_credits}\n\n")
            self.result_text.insert(tk.END, "课程成绩:\n")
            for course in student.academic_data.courses_taken:
                self.result_text.insert(tk.END, f"课程名称: {course.course_name}, 学分: {course.credits}, 成绩: {course.grade}, 是否重修: {'是' if course.is_repeated else '否'}\n")
        else:
            messagebox.showwarning("警告", "学生信息不存在！")

    def import_table(self):
        file_path = filedialog.askopenfilename(filetypes=[("Excel files", "*.xlsx")])
        if file_path:
            self.load_data_from_file(file_path)
            messagebox.showinfo("成功", "表格导入成功！")

    def export_table(self):
        file_path = filedialog.asksaveasfilename(defaultextension=".xlsx", filetypes=[("Excel files", "*.xlsx")])
        if file_path:
            self.student_manager.export_to_excel(file_path)
            messagebox.showinfo("成功", "表格导出成功！")

    def clear_input(self):
        self.entry_student_id.delete(0, tk.END)
        self.entry_name.delete(0, tk.END)
        self.entry_department.delete(0, tk.END)
        self.entry_class.delete(0, tk.END)
        self.entry_query_student_id.delete(0, tk.END)
        self.result_text.delete(1.0, tk.END)


if __name__ == "__main__":
    # 指定源文件路径
    data_file = "students_data.xlsx"
    root = tk.Tk()
    app = StudentGUI(root, data_file)
    root.mainloop()